<?php
	/**
	 * File functions
	 *
	 */
	class File
	{
		/**
		 * ending path names
		 *
		 * @var string
		 */
		public static $backupDate = '';
		
		/**
		 * Variable of general use
		 *
		 * @var array
		 */		
		public static $allData = array();
		
		/**
		 * Folder contents
		 *
		 * @var array
		 */
		public static $treeDirContents;
		
		/**
		 * Gets the functions' list from the file
		 *
		 * @param string $path_to_file path to the file to parse
		 * @return array function list array (name, type, params(name, default_value))
		 */
		static function GetFunctionsListFromFile($path_to_file)
		{
			$file_lines = file($path_to_file);
			
			$functions = array();
			
			$i = 0;
			foreach ($file_lines as &$line)
			{
				$line = trim($line);
				
				//if the line is a function
				if (eregi("^(public|private|protected|static){0,2}( )*function", $line))
				{
					if ($is_code)
					{
						$comment = array();
					}
					
					$is_code = false;
					
					if (empty($class_name)) $class_name = 'noClass';
					
					//function
					$functions[$class_name][$i]['type'] = trim(substr($line, 0, stripos($line, 'function')));
					$functions[$class_name][$i]['full_name'] = trim(eregi_replace("^(public|private|protected|static){0,2}( )*function", '', $line));
					$functions[$class_name][$i]['name'] = trim(substr($functions[$class_name][$i][full_name], 0, stripos($functions[$class_name][$i][full_name], '(')));
					$functions[$class_name][$i]['class_name_string'] = '<b>'.ucfirst(LANG_CLASS).':</b>'.'<p style = \"padding-left: 10px;\">'.$class_name.'</p>';
					//$functions[$i][class_name] = $class_name;
					
					//adding comment for the function
					$functions[$class_name][$i]['comment'] = eregi_replace("'|\"", "", $comment);
					
					//adding location info for the function
					$path_to_file_with_breaks = str_replace(SEPARATOR, ' '.SEPARATOR.' ', $path_to_file);
					$functions[$class_name][$i]['location'] = '<b>'.ucfirst(LANG_LOCATION).':</b>'.'<p style = \"padding-left: 10px;\">'.$path_to_file_with_breaks.'</p>';
					
					if (!empty($comment))
					{
						$functions[$class_name][$i]['comment'] = self::ParseComment($comment);
					}
					else
					{
						$functions[$class_name][$i]['comment'] = array();
					}
					$comment = array();
					
					//parameters array
					$params = 
						explode('$', substr($functions[$class_name][$i]['full_name'], strlen($functions[$class_name][$i][name])+1, $functions[$class_name][$i]['full_name']-1));
					$j = 0;
					foreach ($params as $param)
					{
						if (empty($param)) continue;
						$param = eregi_replace("(\, array )", "", $param);
						$param = eregi_replace("[\, ]", "", $param);
						$param = explode('=', $param);
						$functions[$class_name][$i][params][$j][name] = $param[0];
						$functions[$class_name][$i][params][$j][default_value] = $param[1];
						$j++;
					}
				}
				//if the line is a class declaration
				else if (eregi("^(class)", $line))
				{
					$class_name = eregi_replace("^(class )", "", $line);
				}
				
				//parse comment beginning
				else if (ereg("^(/\*)", $file_lines[$i]))
				{
					$comment = array();
					$is_comment = true;
					$is_code = false;
				}
				
				//parse comment
				else if ($is_comment)
				{
					$is_code = false;
					
					//if end of comment is reached
					if (ereg("^(\*/)", $file_lines[$i])) 
					{
						$is_comment = false;
					}
					else
					{
						//adds to the comment
						if (eregi("^\*", $file_lines[$i]))
						{
							$comment[$i] = $file_lines[$i];
						}
						else
						{
							$is_code = true;
						}
					}
				}
				else
				{
					$is_code = true;
				}

				$i++;
			}
			
			return $functions;
		}
		
		/**
		 * Parses comment strings into description, params, etc
		 *
		 * @param array $comment
		 * @return unknown
		 */
		static function ParseComment(array $comment)
		{
			$i = 0;
			$result = array();
			
			$result[description] = '';
			$result[parameters] = '';
			$result[returns] = '';
						
			//loops through all comment lines
			foreach ($comment as $elem)
			{
				$elem = eregi_replace("^\*( )*", "", $elem);
				$elem = eregi_replace("'|\"", "", $elem);
				if (!eregi("^@", $elem) && (!empty($elem)))
				{
					$result[description] .= '<p style = \"padding-left: 10px; text-align: justify;\">'.$elem.'</p>';
				}
				else
				{
					if (eregi("^@param", $elem))
						$result['parameters'] .= '<p style = \"padding-left: 10px;\">'.eregi_replace("( [$][a-zA-Z0-9_]+)", "<i>\\1</i>", eregi_replace("^@param ", "", $elem)).'</p>';
					else if (eregi("^@return", $elem))
						$result['returns'] .= '<p style = \"padding-left: 10px;\">'.eregi_replace("^@return ", "", $elem).'</p>';
				}
				$i++;
			}
			
			//forms strings for the function description
			$result[description_string] = !empty($result[description]) ? '<b>'.ucfirst(LANG_DESCRIPTION).':</b>'.$result[description] : '';
			$result[parameters_string] = !empty($result[parameters]) ? '<b>'.ucfirst(LANG_PARAMETERS).':</b>'.$result[parameters] : '';
			$result[returns_string] = !empty($result[returns]) ? '<b>'.ucfirst(LANG_RETURNS).':</b>'.$result[returns] : '';
			
			//strips tags from the function's description, parameters, returns
			$result[description] = strip_tags($result[description]);
			$result[parameters] = strip_tags($result[parameters]);
			$result[returns] = strip_tags($result[returns]);
			
			return $result;
		}
		
		/**
		 * Gets the functions' list from the file or directory
		 *
		 * @param string $path_to_dir path to the directory
		 * @return array function list array (name, type, params(name, default_value))
		 */
		static function GetFunctionsList($path_to_dir)
		{
			//if we deal with the file
			if (is_file($path_to_dir))
			{
				$file_name = pathinfo($path_to_dir, PATHINFO_FILENAME);
				
				$funcs = self::GetFunctionsListFromFile($path_to_dir.SEPARATOR.$element);
				if (!empty($funcs))
				{
					$elements[$file_name] = $funcs;
				}
			}
			//if we deal with the directory
			else
			{
				$dir_contents = scandir($path_to_dir);
				foreach ($dir_contents as $element)
				{
					//removes the extention from the file name
					$file_name = self::RemoveFileExtention($element);
					
					//if the file/directory should not be parsed
					if (empty($file_name)||($file_name == '.')||($file_name == 'CVS')) continue;
					
					//if the element is a directory
					if (is_dir($path_to_dir.SEPARATOR.$element))
					{
						self::GetFunctionsList($path_to_dir.SEPARATOR.$element);
					}
					//if the element is a file
					else
					{
						$funcs = self::GetFunctionsListFromFile($path_to_dir.SEPARATOR.$element);
						if (!empty($funcs))
						{
							$elements[$file_name] = $funcs;
						}
					}
				}
			}
			return $elements;
		}
				
		/**
		 * Removes extention from the given file name and returns the file name
		 *
		 * @param string $filename name of the file
		 * @return file name
		 */
		static function RemoveFileExtention($filename)
		{
			return eregi_replace("\.(.)+", '', $filename);
		}
		
		/**
		 * Backup directory (controller, model, views, procedures)
		 *
		 * @param string $page_name controller name
		 * @param string $backup_path path to backup
		 * @param string $file_name PHP file-name
		 * @param string $proc_prefix prefix to stored procedures
		 * @param string $max_quantity of the backups in $backup_path folder
		 * @param string backupDate backuping date&time
		 */
		static function BackupPage($backup_path, $page_name, $file_name, $proc_prefix = 'sp_', $max_quantity = 10, &$backupDate)
		{
			//find backuping time
			self::$backupDate = date("d-m-Y_H-i-s");
			$backupDate = self::$backupDate;
			//here we check if the amount of backups is exceeded and if it is, we delete the oldest version of the page
			if (!(self::CreateBackupFolder($page_name, $backup_path)))
				self::DeleteEndPage($page_name, $backup_path, $max_quantity);
				
			//copying controller, model, views, procedures, verifying if they exist
			//$all_backuped is needed to find out if all four components are in place and can be backuped
			//$all_backuped will return TRUE only if all four components are in place and can be backuped
			$all_backuped = self::ExtractStoredProcedures($backup_path, $page_name, $proc_prefix);
			
			if (is_file("app/controllers/$file_name.php"))
			{
				self::CopyFilesAndFolders("app/controllers/$file_name.php", "{$backup_path}{$page_name}/controllers/{$file_name}_".self::$backupDate.".php");
				$all_backuped = $all_backuped && true;
			}
			else $all_backuped = $all_backuped && false;
			
			if (is_file("app/models/$file_name.php"))
			{
				self::CopyFilesAndFolders("app/models/$file_name.php", "{$backup_path}{$page_name}/models/{$file_name}_".self::$backupDate.".php");
				$all_backuped = $all_backuped && true;
			}
			else $all_backuped = $all_backuped && false;
			
			if (is_dir("app/views/$file_name"))
			{
				self::CopyFilesAndFolders("app/views/$file_name",  "{$backup_path}{$page_name}/views/{$file_name}_".self::$backupDate."/");
				$all_backuped = $all_backuped && true;
			}
			else $all_backuped = $all_backuped && false;
			
			return $all_backuped;
		}
		
		/**
		 * Copy files and folders with all content
		 *
		 * @param string $source what to copy
		 * @param string $target where to copy
		 */
		static function CopyFilesAndFolders($source, $target)
	    {
	    	//check if all parameters are sent
	    	if (!isset($source) or !isset($target))
	    		return false;
	    		
	    	//define if we copy file or folder
	        if (is_dir($source))
	        {
	        	//if target folder exists
	        	if (!is_dir($target))
	            	mkdir($target);	     
	            //get mark of the source folder	      
	            $folder_mark = dir($source);	
	                       
	            while (FALSE !== ($entry = $folder_mark->read()))
	            {
	            	//cut off system marks
	                if ($entry == '.' || $entry == '..')
	                {
	                    continue;
	                }
	                
	                //have deal with subfolders
	                $lower_entry = $source . '/' . $entry;           
	                if (is_dir($lower_entry))
	                {
	                    self::CopyFilesAndFolders($lower_entry, $target . '/' . $entry);
	                    continue;
	                }
	                copy($lower_entry, $target . '/' . $entry) or die(LANG_FILE_OPENING_WAS_NOT_SUCCEDED);
	            }	           
	            $folder_mark->close();
	        }
	        else
	        {
	            copy($source, $target) or die(LANG_FILE_OPENING_WAS_NOT_SUCCEDED);
	        }
	        return true;
	    }

		/**
		 * Extract stored procedures code
		 *
		 * @param array $stored_procedures stored procedures
		 * @return boolean there are or there are not such procedures
		 */
		static function ExtractStoredProcedures($backup_path, $page_name, $proc_prefix)
		{
			global $current_controller;
			//Gets SQL object names (because it is in the global modal, it would work anywhere)
			$proc_names = $current_controller->controller_model->GetSqlObjectNames($proc_prefix.$page_name);
			//if there is no such procedure, stop working of funktion
			if (!isset($proc_names))
				return false;
			$rrrr = self::$backupDate;	
			mkdir($backup_path.$page_name.'/stored_procedures'."/{$page_name}_".self::$backupDate);	
			
			//write a file with the SQL-code
			foreach ($proc_names as $proc_name)
			{
				$procedure = $current_controller->controller_model->GetSqlObjectCode($proc_name);
				
				$fp = fopen($backup_path.$page_name."/stored_procedures"."/{$page_name}_".self::$backupDate."/{$proc_name}_".self::$backupDate.".txt", 'w+') or die(LANG_FILE_OPENING_WAS_NOT_SUCCEDED);

				foreach ($procedure as $proc_text)
				{
					$text .= $proc_text."\n";
				}	
				fwrite($fp, $text);
				fclose($fp);
				$text = '';
			}	
			return true;
		}
		
		/**
		 * create folder to backup files when we make controller, model, etc. again
		 *
		 * @param string $data controller name
		 * @param string $backup_path path to backup
		 * @return boolean  
		 */
		static function CreateBackupFolder($page_name, $backup_path)
		{
			if (!is_dir($backup_path.$page_name))
			{
				if (!is_dir($backup_path))
					mkdir($backup_path);		
				mkdir($backup_path.$page_name);		
				mkdir($backup_path.$page_name.'/controllers');	
				mkdir($backup_path.$page_name.'/models');	
				mkdir($backup_path.$page_name.'/views');	
				mkdir($backup_path.$page_name.'/stored_procedures');				
				clearstatcache();
				return true;
			}	
			else 
			{
				clearstatcache();
				return false;
			}
			
		}
		
		/**
		 * delete the oldest version of the page
		 *
		 * @param string $data controller name
		 * @param string $backup_path path to backup
		 * @param int $max_quantity of backups
		 * @param int $latest_one to delete the oldest or the latest, by default it is oldest
		 * @return string 
		 */
		static function DeleteEndPage($page_name, $backup_path, $max_quantity, $latest_one = 0)
		{
			//subfolders of backup to operate
			$folders = array('/controllers/', '/models/', '/views/', '/stored_procedures/');
			foreach ($folders as $folder)
			{
				//here we get folder content to choose the oldest
				$files = self::GetFolderContent($backup_path.$page_name.$folder);
				
				//if quantity of backups in certain backup folder is not exceeded than we don't take measures 
				$files_quantity = count($files);
				if ($files_quantity < $max_quantity)
				{
					break;
				}
				
				//in search for the latest or oldest
				foreach ($files as $file)
				{
					$stats[$file] = filemtime($backup_path.$page_name.$folder.$file);				
				}
				//to choose in the next the oldest or the latest
				if ($latest_one)
				{
					arsort($stats);
				}
				else 
				{
					asort($stats);
				}	
				reset($stats);
				$file_name = key($stats);
				
				//delete the oldest or the latest
				if(is_dir($backup_path.$page_name.$folder.$file_name))
				{
					self::RemoveDirectory($backup_path.$page_name.$folder.$file_name."/");
				}
				else 
				{
					unlink($backup_path.$page_name.$folder.$file_name);	
				}
			}						
		}
				
		/**
		 * delete a folder
		 *
		 * @param string $dir path to the folder (trailing slash is needed)
		 */
		static function RemoveDirectory($dir) 
		{
	        $dir_contents = scandir($dir);
	        foreach ($dir_contents as $item) {
	            if (is_dir($dir.$item) && $item != '.' && $item != '..') 
	            {
	                self::RemoveDirectory($dir.$item.'/');
	            }
	            elseif (file_exists($dir.$item) && $item != '.' && $item != '..') 
	            {
	                unlink($dir.$item);
	            }
	        }
	        rmdir($dir);
	    }
	    	    
		/**
		 * returns the folder content names
		 *
		 * @param string $path path to the folder
		 * @return array
		 */
		static function GetFolderContent($path)
		{
			//scaning folder
			$files = scandir($path, 1);
			//erase system returnings like '.' and '..'
			foreach($files as $i => $value) 
			{
		        if (substr($value, 0, 1) == '.') 
		        	unset($files[$i]);			        
			}
			return $files;	
		}
	
		/**
		 * returns the folder content names
		 *
		 * @param string $rootDir path to the root folder
		 * @param array $allowext allowed extensions
		 * @param array $notallownames not allowed names
		 * @param array $allData array by reference which collects the root content
		 * @param boolean $removeEmptyFolders not to show folders, which are empty, or haven't allowed files/extensions
		 */
		static function ScanDirectories($rootDir, $allowext, $notallownames, &$allData, $removeEmptyFolders=false) 
		{
	  	    $dirContent = scandir($rootDir);
	  	    
		    //divide files and folders into two groups
		    $dirs = array();
		    $files = array();
		    foreach($dirContent as $key => $content) 
		    {
		    	if ($content == '.' || $content == '..') continue;
//		    		$dirs[$key] = $content;
		    	$path = $rootDir . SEPARATOR . $content;
		    	if (is_file($path))
		    		$files[$key] = $content;
		    	elseif (is_dir($path))	
		    		$dirs[$key] = $content;		    			    			
		    }
		    asort($dirs);
		    asort($files);
		    $dirContent = array_merge($dirs, $files);
		        
	  	    static $i=0;
		    foreach($dirContent as $key => $content) 
		    {
//		        if ($content == '.' || $content == '..') continue;
		        $path = $rootDir . SEPARATOR . $content;
		        $ext = substr($content, strrpos($content, '.') + 1);		        
		        if(self::InArray($ext, $allowext) && is_file($path) && is_readable($path) && !in_array($content, $notallownames)) 
		        {
		            $allData[$i]['name'] = $content;
		            $allData[$i]['path'] = $rootDir;
		            $allData[$i]['sub'] = '';
		        }
		        else if(is_dir($path) && is_readable($path) && !in_array($content, $notallownames)) 
		        {
		        	$allData[$i]['name'] = $content;
		            $allData[$i]['path'] = $rootDir;
		            $allData[$i]['sub'] = array();
		            // recursive callback to open new directory
		            self::scanDirectories($path, $allowext, $notallownames, &$allData[$i]['sub']);
		        }
		        $i++;
		    }
		    if ($removeEmptyFolders)
		    {
			    foreach ($allData as $key=> &$value)
			    {
			    	if (is_array($value['sub']) && empty($value['sub']))
			    	{
			    		unset($allData[$key]);
			    	}
			    }
			    unset($value);
		    }
		}
		
		/**
		 * returns if the file_extension is in array $allowext. If array is empty, all extensions are admited
		 *
		 * @param string $ext file extension
		 * @param array $allowext allowed extensions		 		 
		 * @return boolean
		 */
		static function InArray($ext, $allowext)
		{
			if (empty($allowext))
				return true;
			return in_array($ext, $allowext);
		}
	
	    /**
	     * Parses the file and substitutes #argument_name# with the appropriate value from the $arguments array
	     * @param string $path_to_file
	     * @param array $arguments
	     */
		static function ParseTemplate($path_to_file, array $arguments = array())
	    {
	        $contents = file_get_contents($path_to_file);
	        
	        foreach ($arguments as $name => $value)
	        {
	            $contents = str_replace("#$name#", $value, $contents);
	        }
	        
	        return $contents;	        
	    }
	}
?>